/* mainwindow.cpp -- MainWindow class implementation; MainWindow contains everything
 * mainwindow.cpp is part of Robodrome
 *
 * Copyright (c) 2009 Alex French
 * Robodrome is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * Robodrome is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include "mainwindow.h"

MainWindow::MainWindow(Glib::RefPtr<Gnome::Glade::Xml> refXml)
{
	std::stringstream buf;
	std::stringstream errbuf;
	m_arena_speed = 33;

	refXml->get_widget("main_window", window);
	if (!window) throw std::runtime_error("Couldn't find main_window in interface.glade");

	Gtk::ImageMenuItem* menu_quit;
	refXml->get_widget("menu_quit", menu_quit);
	if (!menu_quit) throw std::runtime_error("Couldn't find menu_quit in interface.glade");
	menu_quit->signal_activate().connect(sigc::mem_fun(*this, &MainWindow::on_menu_quit));

	Gtk::MenuItem* menu_arena;
	refXml->get_widget("menu_arena", menu_arena);
	if (!menu_arena) throw std::runtime_error("Couldn't find menu_arena in interface.glade");
	menu_arena->signal_activate().connect(sigc::mem_fun(*this, &MainWindow::on_menu_arena));

	Gtk::MenuItem* menu_drafting_board;
	refXml->get_widget("menu_drafting_board", menu_drafting_board);
	if (!menu_drafting_board) throw std::runtime_error("Couldn't find menu_drafting_board in interface.glade");
	menu_drafting_board->signal_activate().connect(sigc::mem_fun(*this, &MainWindow::on_menu_drafting_board));

	refXml->get_widget("notebook", tabs);
	if (!tabs) throw std::runtime_error("Couldn't find notebook in interface.glade");

	refXml->get_widget_derived("drawingarea", arena);
	if (!arena) throw std::runtime_error("Couldn't find drawingarea in interface.glade");

	refXml->get_widget("file_open_dialog", file_open_dialog);
	if (!file_open_dialog) throw std::runtime_error("Couldn't find file_open_dialog in interface.glade");

	refXml->get_widget("file_save_dialog", file_save_dialog);
	if (!file_save_dialog) throw std::runtime_error("Couldn't find file_save_dialog in interface.glade");

	refXml->get_widget("menu_open", menu_open);
	if (!menu_open) throw std::runtime_error("Couldn't find menu_open in interface.glade");
	menu_open->signal_activate().connect(sigc::mem_fun(*this, &MainWindow::on_menu_open));

	refXml->get_widget("menu_new", menu_new);
	if (!menu_new) throw std::runtime_error("Couldn't find menu_new in interface.glade");
	menu_new->signal_activate().connect(sigc::mem_fun(*this, &MainWindow::on_menu_new));

	refXml->get_widget("menu_save", menu_save);
	if (!menu_save) throw std::runtime_error("Couldn't find menu_save in interface.glade");
	menu_save->signal_activate().connect(sigc::mem_fun(*this, &MainWindow::on_menu_save));

	refXml->get_widget("menu_save_as", menu_save_as);
	if (!menu_save_as) throw std::runtime_error("Couldn't find menu_save_as in interface.glade");
	menu_save_as->signal_activate().connect(sigc::mem_fun(*this, &MainWindow::on_menu_save_as));

	refXml->get_widget("menu_close", menu_close);
	if (!menu_close) throw std::runtime_error("Couldn't find menu_close in interface.glade");
	menu_close->signal_activate().connect(sigc::mem_fun(*this, &MainWindow::close_robot));

	// Roster info
	for (int i = 0; i < 6; i++) {
		buf << "robot" << i + 1 << "_name";
		errbuf << "Couldn't find " << buf.str() << " in interface.glade";
		refXml->get_widget(buf.str(), robot_name[i]);
		if (!robot_name[i]) throw std::runtime_error(errbuf.str());
		buf.str("");
		errbuf.str("");
		buf << "robot" << i + 1 << "_status";
		errbuf << "Couldn't find " << buf.str() << " in interface.glade";
		refXml->get_widget(buf.str(), robot_status[i]);
		if (!robot_status[i]) throw std::runtime_error(errbuf.str());
		buf.str("");
		errbuf.str("");
		buf << "robot" << i + 1 << "_button";
		errbuf << "Couldn't find " << buf.str() << " in interface.glade";
		refXml->get_widget(buf.str(), robot_button[i]);
		if (!robot_button[i]) throw std::runtime_error(errbuf.str());
		robot_button[i]->signal_clicked().connect(sigc::mem_fun(*this, &MainWindow::on_robot_button_toggled));
		buf.str("");
		errbuf.str("");
	}
	refXml->get_widget("hardware_options_button", hardware_options_button);
	if (!hardware_options_button) throw std::runtime_error("Couldn't find hardware_options_button in interface.glade");
	hardware_options_button->signal_clicked().connect(sigc::mem_fun(*this, &MainWindow::on_hardware_options_button));
	refXml->get_widget_derived("hardware_options_dialog", hardware_options_dialog);
	if (!hardware_options_dialog) throw std::runtime_error("Couldn't find hardware_options_dialog in interface.glade");

	refXml->get_widget("src_view", src_view);
	if (!src_view) throw std::runtime_error("Couldn't find src_view in interface.glade");
	src_view_buf = src_view->get_buffer();

	refXml->get_widget("compile_button", compile_button);
	if (!compile_button) throw std::runtime_error("Couldn't find compile_button in interface.glade");
	compile_button->signal_clicked().connect(sigc::mem_fun(*this, &MainWindow::on_compile_button));

	refXml->get_widget_derived("battle_button", battle_button);
	if (!battle_button) throw std::runtime_error("Couldn't find battle_button in interface.glade");
	battle_button->signal_clicked().connect(sigc::mem_fun(*this, &MainWindow::on_battle_button));

	refXml->get_widget("robot_name_button", robot_name_button);
	if (!battle_button) throw std::runtime_error("Couldn't find robot_name_button in interface.glade");
	robot_name_button->signal_clicked().connect(sigc::mem_fun(*this, &MainWindow::on_robot_name_button));

	refXml->get_widget("robot_name_dialog", robot_name_dialog);
	if (!robot_name_dialog) throw std::runtime_error("Couldn't find robot_name_dialog in interface.glade");

	refXml->get_widget("robot_name_entry", robot_name_entry);
	if (!robot_name_entry) throw std::runtime_error("Couldn't find robot_name_entry in interface.glade");

	refXml->get_widget("menu_play_sounds", menu_play_sounds);
	if (!menu_play_sounds) throw std::runtime_error("Couldn't find menu_play_sounds in interface.glade");
	menu_play_sounds->signal_activate().connect(sigc::mem_fun(*this, &MainWindow::on_menu_play_sounds));

	for (int i = 0; i < 5; i++) {
		buf << "menu_speed" << i;
		errbuf << "Couldn't find " << buf.str() << " in interface.glade";
		refXml->get_widget(buf.str(), menu_speed[i]);
		if (!menu_speed[i]) throw std::runtime_error(errbuf.str());
		menu_speed[i]->signal_activate().connect(sigc::mem_fun(*this, &MainWindow::on_menu_speed));
		buf.str("");
		errbuf.str("");
	}

	refXml->get_widget("menu_display_battle", menu_display_battle);
	if (!menu_display_battle) throw std::runtime_error("Couldn't find menu_display_battle in interface.glade");
	menu_display_battle->signal_activate().connect(sigc::mem_fun(*this, &MainWindow::on_menu_display_battle));

	for (int i = 0; i < 3; i++) {
		buf << "menu_debug" << i;
		errbuf << "Couldn't find " << buf.str() << " in interface.glade";
		refXml->get_widget(buf.str(), menu_debug[i]);
		if (!menu_debug[i]) throw std::runtime_error(errbuf.str());
		menu_debug[i]->signal_activate().connect(sigc::mem_fun(*this, &MainWindow::on_menu_debug));
		buf.str("");
		errbuf.str("");
	}

	refXml->get_widget_derived("debugger", debugger);
	if (!hardware_options_dialog) throw std::runtime_error("Couldn't find debugger in interface.glade");
}

MainWindow::~MainWindow(void)
{
	delete window;
}

void MainWindow::on_menu_quit(void)
{
	window->hide();
}

void MainWindow::on_menu_arena(void)
{
	tabs->set_current_page(0);
}

void MainWindow::on_menu_drafting_board(void)
{
	tabs->set_current_page(1);
}

void MainWindow::on_menu_open(void)
{
	int result = file_open_dialog->run();

	switch (result) {
		case 0: {
			this->add_new_robot();
			players[selected_player].set_path(file_open_dialog->get_filename());
			players[selected_player].open();
			src_view_buf->set_text(players[selected_player].get_src());
			this->update_roster();
			break;
		}
		default: {
			 break;
		}
	}
	file_open_dialog->hide();
	this->update_roster();
}

void MainWindow::on_menu_new(void)
{
	this->add_new_robot();
}

void MainWindow::on_menu_save(void)
{
	if (players[selected_player].get_path().empty()) {
		this->on_menu_save_as();
	} else {
		players[selected_player].set_src(src_view_buf->get_text().raw());
		players[selected_player].save();
	}
}

void MainWindow::on_menu_save_as(void)
{
	int result = file_save_dialog->run();

	switch (result) {
		case 0: {
			players[selected_player].set_path(file_save_dialog->get_filename().c_str());
			this->on_menu_save();
			break;
		}
	}
	file_save_dialog->hide();
}

void MainWindow::on_robot_name_button(void)
{
	robot_name_entry->set_text(players[selected_player].get_name());

	int result = robot_name_dialog->run();

	if (result == 1) {
		players[selected_player].set_name(robot_name_entry->get_text());
	}

	robot_name_dialog->hide();
	this->update_roster();
}

void MainWindow::on_hardware_options_button(void)
{
	hardware_options_dialog->init();
	hardware_options_dialog->run();
	hardware_options_dialog->hide();
}

void MainWindow::add_new_robot(void)
{
	// activate buttons
	menu_save->set_sensitive(true);
	menu_save_as->set_sensitive(true);
	menu_close->set_sensitive(true);
	hardware_options_button->set_sensitive(true);
	compile_button->set_sensitive(true);
	battle_button->set_sensitive(true);
	robot_name_button->set_sensitive(true);

	players.push_back(Robot());
	robot_button[players.size() - 1]->set_active(); // select newly added robot
	this->update_roster();
	// disable new and open when max has been reached
	if (players.size() >= 6) {
		menu_new->set_sensitive(false);
		menu_open->set_sensitive(false);
	} else {
		menu_new->set_sensitive(true);
		menu_open->set_sensitive(true);
	}
}
void MainWindow::close_robot(void)
{
	if (!players.size())
		return;

	if (!players[selected_player].get_modified()) {
		players.erase(players.begin() + selected_player);
	} else {
		players.erase(players.begin() + selected_player);
		// activate "are you sure?" dialog
	}
	if (players.size() == selected_player && selected_player != 0) { // adjust selection if last robot in the roster is closed
		selected_player--;
	}
	if (!players.size()) { // no robots left, disable save, close, and hardware options buttons, clear drafting board
		menu_save->set_sensitive(false);
		menu_save_as->set_sensitive(false);
		menu_close->set_sensitive(false);
		hardware_options_button->set_sensitive(false);
		compile_button->set_sensitive(false);
		battle_button->set_sensitive(false);
		robot_name_button->set_sensitive(false);
		src_view_buf->set_text("");
	} else {
		robot_button[selected_player]->clicked();
	}

	// activate add_robot buttons
	menu_new->set_sensitive(true);
	menu_open->set_sensitive(true);
	update_roster();
}

void MainWindow::update_roster(void)
{
	std::stringstream buf;
	int i;

	for (i=0; i < (int)players.size(); i++) {
		// activate button, name, status, and image
		robot_button[i]->set_sensitive(true);
		robot_name[i]->set_sensitive(true);
		robot_status[i]->set_sensitive(true);
		buf << "<b>" << players[i].get_name() << "</b>";
		robot_name[i]->set_label(buf.str());
		buf.str("");
		if (players[i].alive()) {
			buf << "<tt>Energy: " << players[i].get_energy() << std::endl;
		} else {
			buf << "<tt>Dead: " << players[i].get_killer() << std::endl;
		}
		if (players[i].get_shield())
			buf << "Shield: " << players[i].get_shield() << std::endl;
		else
			buf << "Damage: " << players[i].get_damage() << std::endl;
		buf << "Points: " << players[i].get_history() << "</tt>";
		robot_status[i]->set_label(buf.str());
		buf.str("");
	}

	while (i < 6) { // make sure the remaining robots are deactivated and cleared
		robot_button[i]->set_sensitive(false);
		robot_name[i]->set_sensitive(false);
		robot_status[i]->set_sensitive(false);
		buf << "Not Selected";
		robot_name[i]->set_label(buf.str()); 
		buf.str("");
		robot_status[i]->set_label(buf.str());
		i++;
	}
}

void MainWindow::on_robot_button_toggled(void)
{
	for (int i = 0; i < 6; i++) {
		if (robot_button[i]->get_active()) {
			selected_player = i;
			src_view_buf->set_text(players[selected_player].get_src());
			return;
		}
	}
}

void MainWindow::on_compile_button(void)
{
	std::stringstream message;
	players[selected_player].set_src(src_view_buf->get_text().raw());
	std::string err = players[selected_player].compile();
	if (!err.empty()) {
		message << "<b>Error in " << players[selected_player].get_name() << "</b>" << std::endl;
		message << err;
		error_message->set_markup(message.str());
		error_dialog->run();
		error_dialog->hide();
	}
}

void MainWindow::on_battle_button(void)
{
	if (battle_button->get_active()) {
		menu_new->set_sensitive(false);
		menu_open->set_sensitive(false);
		menu_save->set_sensitive(false);
		menu_save_as->set_sensitive(false);
		menu_close->set_sensitive(false);
		hardware_options_button->set_sensitive(false);
		compile_button->set_sensitive(false);
		robot_name_button->set_sensitive(false);
		arena->init();
		Glib::signal_timeout().connect_once(sigc::mem_fun(*this, &MainWindow::step_battle), m_arena_speed, 100);
	} else {
		menu_new->set_sensitive(true);
		menu_open->set_sensitive(true);
		menu_save->set_sensitive(true);
		menu_save_as->set_sensitive(true);
		menu_close->set_sensitive(true);
		hardware_options_button->set_sensitive(true);
		compile_button->set_sensitive(true);
		robot_name_button->set_sensitive(true);
	}
}

//bool MainWindow::step_battle(void)
void MainWindow::step_battle(void)
{
	int alive = 0;
	if (battle_button->get_active()) {
		arena->step_world();
		for (int i=0; i < (int)players.size(); i++) {
			if (players[i].alive())
				alive++;
		}
		if (display_enabled) {
			arena->queue_draw();
			this->update_roster();
		}
		if (alive <= 1 && alive < (int)players.size()) battle_button->clicked(); // no more robots, end simulation
		Glib::signal_timeout().connect_once(sigc::mem_fun(*this, &MainWindow::step_battle), m_arena_speed, Glib::PRIORITY_LOW);
	} else if (!display_enabled) { // show results at end of battle if display is disabled
		arena->queue_draw();
		this->update_roster();
	}
}

void MainWindow::on_menu_play_sounds(void)
{
	sounds_enabled = menu_play_sounds->get_active();
}

void MainWindow::on_menu_speed(void)
{
	for (int i = 0; i < 5; i++) {
		if (menu_speed[i]->get_active()) {
			switch (i) {
				case 4:
					m_arena_speed = 0;
					break;
				case 3:
					m_arena_speed = 33;
					break;
				case 2:
					m_arena_speed = 66;
					break;
				case 1:
					m_arena_speed = 100;
					break;
				case 0:
					m_arena_speed = 250;
					break;
			}
			return;
		}
	}
}

void MainWindow::on_menu_display_battle(void)
{
	display_enabled = menu_display_battle->get_active();
}

void MainWindow::on_menu_debug(void)
{
	for (int i = 0; i < 3; i++) {
		if (menu_debug[i]->get_active()) {
			switch (i) {
				case 0:
					use_debugger = DEBUG_OFF;
					debugger->hide();
					break;
				case 1:
					use_debugger = selected_player;
					debugger->present();
					break;
				case 2:
					use_debugger = DEBUG_ALL;
					debugger->present();
					break;
			}
			return;
		}
	}
}
